/*
 * Unicast.cpp
 *
 *  Created on: 08/ott/2013
 *      Author: alessandrob
 */
#include<iostream>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<opencv2/opencv.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<opencv2/highgui/highgui.hpp>
#include "Utility.h"
#include "brisk/brisk.h"
//#include "brisk.h"
#define PORT 5002
#define TEST 20

void unicastF(int clientsNumber,float overlapPerc,int loads,int threshold,int octave)
{
	int i,size,sockfd,countClientsConnected=0,y=0,row,k,dataSent,cols,rows,dataReceived,overlap;
	struct sockaddr_in server_address, client_address;
	vector<sockaddr_in> clients;
	struct list_nodes nodeList[clientsNumber+1];
	unsigned int slen=sizeof(client_address);
	float bandwidth;
	char* ipAddress;
	char infoPacket[9];
	char ack;
	char sync,command[35];
	double sumProc=0,det_time;
	Mat splitImage[clientsNumber+1];
	Mat original,image;
	Mat distributedKPs,distributedDescriptorsA,distributedDescriptorsB;
	vector<int> params = vector<int>(2);
	params[0]=100;
	vector<uchar> data;
	cout<<"UNICAST MODE. Setting up BRISK."<<endl;
	vector<KeyPoint> distributedKpa,distributedKpb;

  //Setting up BRISK implementazione degli autori
	Ptr<FeatureDetector> detector = new cv::BriskFeatureDetector(threshold,octave);
	Ptr<DescriptorExtractor> descriptorExtractor = new cv::BriskDescriptorExtractor();

  //Opening file per il salvataggio dei dati
	Mat descriptors,distributedDescriptors;
	vector<KeyPoint> kp,distributedKp;
	FILE* file = fopen("/home/ubuntu/testUnicast.txt","a+");
	if(file == NULL)
		return;
	FILE* fileBandwidth = fopen("/home/ubuntu/bandwidthUnicastIdeale.txt","a+");
	if(fileBandwidth == NULL)
		return;

  //Caricamento dell'immagine da memoria
	original = imread("/home/ubuntu/zubud1.png",0);

	cout<<"BRISK: CAMERA ONLY"<<endl;
	cout<<"Timer started"<<endl;
	double processing_time;

	//Esecuzione BRISK (la prima volta il tempo di esecuzione è sempre maggiore delle volte successive).
	detector->detect(original,kp);
	descriptorExtractor->compute(original,kp,descriptors);
	Mat OriginalKeypoints;
	drawKeypoints(original,kp,OriginalKeypoints);
	imwrite("/home/ubuntu/OriginalKeypoints.jpg",OriginalKeypoints,params);
	detector->detect(original,distributedKpa);
	descriptorExtractor->compute(original,distributedKpa,distributedDescriptors);
	//COMPUTING BRISK (ten times)

  //Esecuzione di BRISK 10 volte, per calcolare la velocità del processore in bit/s
	for(int i=0;i<TEST;i++)
	{
		det_time = (double)getTickCount();
		detector->detect(original,kp);
		descriptorExtractor->compute(original,kp,descriptors);
		processing_time = ((double)getTickCount() - det_time)/getTickFrequency();
		cout<<kp.size()<<endl;
		//Write on a text file
		fprintf(file,"%f ",(processing_time*1000));
		sumProc += processing_time;
	}
	fprintf(file,"\n");
	processing_time = (sumProc/TEST);
	cout<<"Processing time: "<<processing_time*1000<<"ms"<<endl;
	cout<<"Keypoints detected: "<<kp.size()<<endl;
	//CPU speed (bit/s)*/
	for(int i=0;i<clientsNumber+1;i++)
		nodeList[i].CPUspeed = (int)((original.cols*original.rows*8)/processing_time);
		//nodeList[i].CPUspeed = 1657181;
	cout<<"Camera CPU speed: "<<nodeList[0].CPUspeed<<"bps"<<endl;

	if(clientsNumber>0)
	{
		cout<<"Setting up TCP server"<<endl;
		//TCP Socket creation
		if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1)
			printErrorLog("Server socket creation error. ");
		cout<<"Server TCP socket created correctly."<<endl;
		memset((char *) &server_address, 0, sizeof(server_address));
		server_address.sin_family = AF_INET;
		server_address.sin_port = htons(PORT);
		server_address.sin_addr.s_addr = htonl(INADDR_ANY);

		//Binding on port: 5002
		if (bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address))==-1)
			printErrorLog("Wrong binding. The server is NOT running");
		cout<<"Correct binding. The server is working correctly, listening for connections on port: 5002."<< endl;

		//Ciclo in attesa delle connessioni di tutti i client.
		listen(sockfd,clientsNumber);
		for(int test=0;test<TEST;test++)
		{
			row = 0;
			y = 0;
			countClientsConnected = 0;
			while(countClientsConnected!=clientsNumber)
			{
				if((nodeList[countClientsConnected+1].socket = accept(sockfd,(struct sockaddr*)&client_address,&slen))==-1)
					printErrorLog("Invalid client socket");
				//if(recv(nodeList[countClientsConnected+1].socket, &nodeList[countClientsConnected+1].CPUspeed, sizeof(int), 0)<0)
				//	printErrorLog("CPU speed not received correctly");
				printf("Beaglebone connected: %s:%d\n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
				clients.push_back(client_address);
				countClientsConnected++;
			}

			//Per tutti i client connessi, il server effettua il test della banda media.
			for(i=0;i<clientsNumber;i++)
			{
				ipAddress = inet_ntoa(clients[i].sin_addr);
				strcpy(command,"iperf -c");
				strcat(command,inet_ntoa(clients[i].sin_addr));
				strcat(command,"  >/home/ubuntu/iperfLog.txt");
				cout<<"Starting bandwidth test: "<<inet_ntoa(clients[i].sin_addr)<<endl;
				system(command);
				//Parsing del file per estrarre il valore della banda calcolata.
				bandwidth=getBandwidth(false);
				if(bandwidth==0)
					printErrorLog("Network performance report file doesn't exist");
				fprintf(fileBandwidth,"%f ",bandwidth);
				//strcpy(nodeList[i+1].ipAddress,ipAddress);
				nodeList[i+1].bandwidth=bandwidth*1000000;
				printf("Bandwidth: %fMbps\n",nodeList[i+1].bandwidth/1000000);
			}

			//Distributed BRISK
			cout<<"DISTRIBUTED BRISK"<<endl;
			cout<<"Timer started"<<endl;
			det_time=(double)getTickCount();
			//Sincronizzazione con tutti i client connessi. A turno, il server invia un byte per comunicare 
      //lo start del timer locale.   
			int s;
			sync = 's';
			for(k=0;k<clientsNumber;k++)
			{
				s = nodeList[k+1].socket;
				if(send(s,&sync,sizeof(sync),0)==-1)
					printErrorLog("Synchronization failed");
			}
			double sync_time = ((double)getTickCount() - det_time)/getTickFrequency();
			cout<<"Synchronization time: "<<sync_time*1000<<"ms"<<endl;
         
			//Ordinamento dei nodi in modo decrescente in base alla banda.
			if(clientsNumber>1)
				orderNodes(clientsNumber,nodeList);
           
			//Calcolo dei load, basandosi sul modello teorico.
			computeLoadUnicast(clientsNumber,nodeList,overlapPerc,loads);
			overlap = (int)(original.rows*overlapPerc)/100;
			//Splitting per righe
			for(i=0;i<clientsNumber+1;i++)
			{
				row = floor((original.rows*nodeList[i].load)/100);
				cout<<"row: "<<row<<endl;
				nodeList[i].width = original.cols;
				cout<<"From "<<y<<" to "<<y+row<<endl;
				if(i==clientsNumber)
				{
					if((y+row)!=original.rows)
					{
						row=original.rows-y;
						cout<<"row: "<<row<<endl;
					}
					splitImage[i] = Mat(original,Rect_<int>(0,y,original.cols,row));
				}
				else
				{
					splitImage[i] = Mat(original,Rect_<int>(0,y,original.cols,row+overlap));
				}
				y=y+row;
				nodeList[i].height = y;
				cout<<"Slice"<<i<<" height: "<<y<<endl;
			}
			double splittingtime = ((double)getTickCount() - det_time)/getTickFrequency();
			cout<<"Splitting time: T+"<<splittingtime*1000<<"ms"<<endl;
			double sendtime;
         
			//Per ogni client, invio della frazione di immagine.
			for(i=0;i<clientsNumber;i++)
			{
        //Pacchetto di informazione, contenente dimensioni della frazione di immagine. 
				cols = splitImage[i+1].cols;
				rows = splitImage[i+1].rows;
				infoPacket[0] = (cols>>24);
				infoPacket[1] = (cols>>16);
				infoPacket[2] = (cols>>8);
				infoPacket[3] = (cols);
				infoPacket[4] = (rows>>24);
				infoPacket[5] = (rows>>16);
				infoPacket[6] = (rows>>8);
				infoPacket[7] = (rows);
				if(i==(clientsNumber-1))
					//last client
					infoPacket[8] = 1;
				else
					infoPacket[8] = 0;
				s = nodeList[i+1].socket;
				if(send(s,infoPacket,sizeof(char)*9,0)!=sizeof(char)*9)
					printErrorLog("Info packet not sent correctly");
				uchar support[cols*rows];
				memcpy(&support,splitImage[i+1].data,cols*rows);
				double starttime = ((double)getTickCount() - det_time)/getTickFrequency();
				cout<<"Start sending time:"/* (client "<<nodeList[i+1].ipAddress<<"):*/" T+"<<starttime*1000<<"ms"<<endl;
				dataSent = send(s,support,cols*rows,0);
				sendtime =((double)getTickCount() - det_time)/getTickFrequency();
				cout<<"Sending time"/* (client "<<nodeList[i+1].ipAddress<<"):*/" T+"<<sendtime*1000<<"ms"<<endl;
				//cout<<"Number of bytes actually sent: "<<dataSent<<endl;
				if(dataSent!=(cols*rows))
					printErrorLog("Data not sent correctly");
			}
         
			//Per il problema del tempo di invio (scrittura sul buffer), affinchè il modello teorico
      //venga rispettato, l'ultimo client invia un byte al server per comunicargli l'effettiva
      //ricezione della propria frazione di immagine. Il server, dopo averi ricevuto questo byte
      //manda in esecuzione BRISK.   
			s = nodeList[clientsNumber].socket;
			if(recv(s, &ack, sizeof(char), 0)<0)
				printErrorLog("ACK not received correctly");
			cout<<"ACK received. Ready to compute BRISK"<<endl;
			if(ack=='f')
			{
				double ack_time = ((double)getTickCount() - det_time)/getTickFrequency();
				cout<<"ACK time: T+"<<ack_time*1000<<"ms"<<endl;
				detector->detect(splitImage[0],distributedKpa);
				descriptorExtractor->compute(splitImage[0],distributedKpa,distributedDescriptorsA);
				double final_time = ((double)getTickCount() - det_time)/getTickFrequency();
				cout<<"Final time: T+"<<final_time*1000<<"ms"<<endl;
            
				size = distributedKpa.size();
				cout<<"Keypoints found: "<<size<<endl;

				//Timer stopped
				nodeList[0].keypointsDetected = distributedKpa.size();
				nodeList[0].time = final_time*1000;
				for(i=0;i<nodeList[0].keypointsDetected;i++)
				{
					nodeList[0].keypoints.push_back(distributedKpa[i].pt.x);
					nodeList[0].keypoints.push_back(distributedKpa[i].pt.y);
				}
				//copying distributed descriptors
				nodeList[0].descriptorsSize = nodeList[0].keypointsDetected*64;
				for(i=0;i<nodeList[0].descriptorsSize;i++)
					nodeList[0].descriptors.push_back(distributedDescriptorsA.data[i]);
				/*nodeList[1].descriptorsSize = nodeList[1].keypointsDetected*64;
				for(i=0;i<nodeList[1].descriptorsSize;i++)
						nodeList[1].descriptors.push_back(distributedDescriptorsB.data[i]);*/
				//Waiting for the clients processing final times

				//RECEIVING DATA AND CHECK

				cout<<"RECEIVING DATA FROM THE CLIENTS"<<endl;
				//int keypointsDetected;
				double time = receiveData(clientsNumber,nodeList);
				fprintf(file,"%f ",time);
				//int result = check(clientsNumber,nodeList,kp,descriptors);
				/*fprintf(file,"Distributed\n");
				for(int i=0;i<nodeList[0].keypointsDetected*2;i=i+2)
				{
					fprintf(file,"%f %f\n",nodeList[0].keypoints[i],nodeList[0].keypoints[i+1]);
				}
				int height = nodeList[0].height;
				for(int i=0;i<nodeList[1].keypointsDetected*2;i=i+2)
				{
					fprintf(file,"%f %f\n",nodeList[1].keypoints[i],nodeList[1].keypoints[i+1]+height);
				}*/
				/*
				if(result==0)
					cout<<"Check correct"<<endl;
				else if(result==1)
					printErrorLog("Keypoints number not corrected");
				else if(result==2)
					printErrorLog("At least one keypoint different");
				*/
			}
		}
		close(sockfd);
	}
	fprintf(fileBandwidth,"\n");
	fprintf(file,"\n");
	fclose(fileBandwidth);
	fclose(file);
}


